package org.sample.jdbc.commons.jdbc;


import org.sample.jdbc.commons.bean.LRUMap;

import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

public class CacheDalClient extends PoolDalClient {

    private static volatile Map<String, Object> dataCache = new LRUMap<>(64);

    public static final String cache = "cache";

    public CacheDalClient(Scanner scanner) {
        super(scanner);
    }

    public CacheDalClient(String driver, String url, String username, String password) {
        super(driver, url, username, password);
    }

    public CacheDalClient(String key) {
        super(key);
    }

    private String getKey(String sqlName, Object... args) {
        StringBuilder key = new StringBuilder(sqlName);
        for (Object arg : args)
            key.append(arg);
        return key.toString();
    }

    @Override
    public String getString(String sqlName, Object... args) throws SQLException {
        return find(() -> super.getString(sqlName, args), sqlName, args);
    }

    @Override
    public List<String> findColumn(String sqlName, Object... args) throws SQLException {
        return find(() -> super.findColumn(sqlName, args), sqlName, args);
    }

    @Override
    public List<String[]> find(String sqlName, Object... args) throws SQLException {
        return find(() -> super.find(sqlName, args), sqlName, args);
    }

    private <T> T find(Callback<T> callback, String sqlName, Object... args) throws SQLException {
        if (!sqlName.startsWith(cache))
            return callback.call();
        sqlName = sqlName.substring(0, cache.length()).trim();
        String key = getKey(sqlName, args);
        T result = (T) dataCache.get(key);
        if (result == null) {
            synchronized (dataCache) {
                if (result == null) {
                    result = callback.call();
                    dataCache.put(key, result);
                }
            }
        }
        return result;
    }

    @FunctionalInterface
    public interface Callback<T> {
        T call() throws SQLException;
    }
}